package busterminal.command.trip;

import busterminal.command.AbstractCommand;
import busterminal.entity.Bus;
import busterminal.entity.Route;
import busterminal.entity.Trip;
import busterminal.entity.TripImplementation;
import busterminal.entity.service.TripState;
import busterminal.exception.BusinessException;
import busterminal.persistence.BusDAO;
import busterminal.persistence.RouteDAO;
import busterminal.persistence.TripDAO;
import busterminal.services.ServiceLocator;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

/**
 * Команда для добавления нового рейса
 */
public class CreateTripCommand extends AbstractCommand {

    /**
     * Имя параметра "Маршрут, по которому осуществляется рейс"
     */
    private final static String PARAM_ROUTE_ID = "route id";
    /**
     * Имя параметра "Состояние рейса"
     */
    private final static String PARAM_TRIP_STATE = "trip state";
    /**
     * Имя параметра "Дата осуществления рейса"
     */
    private final static String PARAM_TRIP_DATE = "trip date (in DD.MM.YYYY format)";
    /**
     * Имя параметра "Автобус, который задействован в рейсе"
     */
    private final static String PARAM_BUS_ID = "bus id";
    /**
     * Имя параметра "Имя водителя"
     */
    private final static String PARAM_DRIVER_NAME = "driver name";
    /**
     * Имя параметра "Имя кондуктора"
     */
    private final static String PARAM_CONDUCTOR_NAME = "conductor name";
    /**
     * Регулярное выражение для проверки корректности ввода даты в формате <code>DD.MM.YYYY</code>
     */
    private static final String CORRECT_DATE_FORMAT = "^((0[1-9]|[12]\\d|3[01])\\.(0[13578]|1[02])\\.((19|[2-9]\\d)\\d{2}))|((0[1-9]|[12]\\d|30)\\.(0[13456789]|1[012])\\.((19|[2-9]\\d)\\d{2}))|((0[1-9]|1\\d|2[0-8])\\.02\\.((19|[2-9]\\d)\\d{2}))|(29\\.02\\.((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))$";
    /**
     * Регулярное выражение для проверки корректности ввода числа
     */
    private static final String CORRECT_NUMBER_FORMAT = "^[1-9]([0-9]*)$";

    /**
     * Конструктор по умолчанию
     */
    public CreateTripCommand() {
        super("Add new trip");
    }

    /**
     * Получить список имён параметров
     * @return список имён параметров
     */
    @Override
    public List<String> getParametersNames() {
        return Arrays.asList(PARAM_ROUTE_ID, PARAM_TRIP_STATE, PARAM_TRIP_DATE, PARAM_BUS_ID, PARAM_DRIVER_NAME, PARAM_CONDUCTOR_NAME);
    }

    /**
     * Выполнить команду
     */
    @Override
    public void execute() {
        Trip trip = new TripImplementation();

        String paramRouteId = parameters.get(PARAM_ROUTE_ID).trim();
        if (!paramRouteId.matches(CORRECT_NUMBER_FORMAT)) {
            throw new BusinessException("Incorrect route id='" + paramRouteId + "'");
        }
        int routeId;
        try {
            routeId = Integer.parseInt(paramRouteId);
        } catch (Exception e) {
            throw new BusinessException("Incorrect route id='" + paramRouteId + "'; " + e.toString(), e);
        }
        Route route = ServiceLocator.getService(RouteDAO.class).read(routeId);
        if (route == null) {
            throw new BusinessException("Cannot find route with id='" + routeId + "'");
        }
        trip.setRoute(route);

        if (parameters.get(PARAM_TRIP_STATE).trim().isEmpty()) {
            throw new BusinessException("Incorrect trip state: '" + parameters.get(PARAM_TRIP_STATE).trim() + "'");
        }
        try {
            trip.setTripState(TripState.valueOf(parameters.get(PARAM_TRIP_STATE).trim().toUpperCase()));
        } catch (Exception e) {
            throw new BusinessException("Incorrect trip state: '" + parameters.get(PARAM_TRIP_STATE).trim() + "'; " + e.toString(), e);
        }

        if (!parameters.get(PARAM_TRIP_DATE).trim().matches(CORRECT_DATE_FORMAT)) {
            throw new BusinessException("Incorrect date: '" + parameters.get(PARAM_TRIP_DATE).trim() + "'");
        }
        try {
            trip.setTripDate(new SimpleDateFormat("dd.MM.yyyy").parse(parameters.get(PARAM_TRIP_DATE).trim()));
        } catch (Exception e) {
            throw new BusinessException("Incorrect date: '" + parameters.get(PARAM_TRIP_DATE).trim() + "'; " + e.toString(), e);
        }

        String paramBusId = parameters.get(PARAM_BUS_ID).trim();
        if (!paramBusId.matches(CORRECT_NUMBER_FORMAT)) {
            throw new BusinessException("Incorrect bus id='" + paramBusId + "'");
        }
        int busId;
        try {
            busId = Integer.parseInt(paramBusId);
        } catch (Exception e) {
            throw new BusinessException("Incorrect bus id='" + paramBusId + "'; " + e.toString(), e);
        }
        Bus bus = ServiceLocator.getService(BusDAO.class).read(busId);
        if (bus == null) {
        }
        trip.setBus(bus);

        if (parameters.get(PARAM_DRIVER_NAME).trim().isEmpty()) {
            throw new BusinessException("You should define driver name");
        }
        trip.setDriverName(parameters.get(PARAM_DRIVER_NAME).trim());

        if (parameters.get(PARAM_CONDUCTOR_NAME).trim().isEmpty()) {
            throw new BusinessException("You should define conductor name");
        }
        trip.setConductorName(parameters.get(PARAM_CONDUCTOR_NAME).trim());

        ServiceLocator.getService(TripDAO.class).create(trip);
    }

    /**
     * Вывести список доступных вариантов параметра с фиксированным набором значений
     * @param parameterName имя параметра с фиксированным набором значений
     */
    @Override
    public void showAccessableVariants(String parameterName) {
        if (PARAM_TRIP_STATE.equals(parameterName)) {
            System.out.println("Accessable variants: " + TripState.getValues());
        }
    }
}
